/*
 Copyright (c) Whitemice Consulting, 2007, 2008, 2009
 Adam Tauno Williams <awilliam@whitemice.org>
 License: MIT/X11

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Whitemice.ZOGI;

namespace Whitemice.ZOGI.Backend
{
  public class Cache
  {
    float requests, hits;
    protected Dictionary<int, Whitemice.ZOGI.Entity> objects;
    protected List<Whitemice.ZOGI.CompanyValue>      companyValues;

    
    public Cache()
    {
      requests = 0;
      hits = 0;
      objects = new Dictionary<int, Whitemice.ZOGI.Entity>();
      companyValues = new List<Whitemice.ZOGI.CompanyValue>(1024);
    }
    
    /*
    public ~Cache
    {
      db.Close();
    }
    */
		internal bool Has(int objectId, int detail, out Entity entity)
		{
		  bool result = false;
		  
  		lock(this)
	  	{
        requests++;
		    entity = Get(objectId);
    	  if (entity == null)
    	  {
    	    return false;
    	  }
        if (entity.FirstClass)
        {
			    if (entity.IsLevel(detail))
          {
			      result = true;
          }
	  		} else result = true;
        if(result)
          hits++;
	    } // end lock
      return result;
		} // end CacheCheck

    public int HitRatio
    {
      get
      {
        if (requests == 0)
          return 0;
        return Convert.ToInt32(((hits / requests) * 100));
      }
    }
		      
    internal T Get<T>(int objectId) where T : Entity
    {
      lock(this)
      {
        var x = this.Get(objectId);
        if ((x != null) && (x is T))
          return (x as T);
        //if ((objects.ContainsKey(objectId)) &&
        //    (objects[objectId] is T))
        //  return (objects[objectId] as T);
        return null;
      }
    } // end Get

    internal bool Delete(Entity _entity)
    {
      bool retVal = false;
      lock(this)
      {
        if (objects.ContainsKey(_entity.ObjectId))
        {
          objects.Remove(_entity.ObjectId);
          retVal =  true;
        } else retVal = false;
      }
      return retVal;
    } // end Get
    
    internal Entity Get(int objectId)
    {
			Entity              result;
			
			result = null;
      lock(this)
      {
        if (objects.ContainsKey(objectId))
          result = objects[objectId];
      }
      return result;
    } // end Get
    
    internal void Store(Entity entity)
    {
      if (entity == null)
        return;
      lock(this)
      {
        if (objects.ContainsKey(entity.ObjectId))
        {
          // existing entity
          if (objects[entity.ObjectId] == entity)
            return;
          throw new Exception(
            string.Format("Storing duplicate entity {0}#{1}",
              entity.EntityName,
              entity.ObjectId));
        } else 
          {
            // new entity
            objects.Add(entity.ObjectId, entity);
            if (entity is Whitemice.ZOGI.CompanyValue)
            {               
              companyValues.Add(entity as Whitemice.ZOGI.CompanyValue);
              return;
            }
          } // end new-entity
      } // end lock
    } // end Store
    
    internal void Commit()
    {
    }
    
		internal CompanyValue LegacyXAFromCache(string name, 
		                                        int companyId)
		{			
			IEnumerable<CompanyValue> batch;
			CompanyValue              result;
			
			lock(this)
			{
		    batch = from CompanyValue p 
		             in objects.Values.OfType<Whitemice.ZOGI.CompanyValue>()
	               where p.CompanyObjectId == companyId &&
		                   p.Name == name.Trim()
		             select p;
	    	if (batch.Count() == 1)
	    	{
	  	    result = batch.ElementAt(0);
	  	  } else result = null;
	  	  batch = null;
	  	}
	  	return result;
		} // end LegacyXAFromCache 
		
		internal IEnumerable<T> GetChildrenOf<T>(int objectId) 
		  where T : Whitemice.ZOGI.Entity
		{
      List<T>  batch;
      lock(this)
      { 
		    batch = new List<T>(from T e in objects.Values.OfType<T>()
		                        where e.ParentObjectId == objectId
                            select e);
      }
      this.ZLOG(string.Format("Cache returning {0} {1} children of {2}", batch.Count(), typeof(T), objectId));
		  return batch;
		} // end GetChildrenOf
		
		internal PhoneNumber GetPhoneNumber(int objectId, string type)
		{
		  
		  Whitemice.ZOGI.PhoneNumber  result;
		  lock(this)
		  {
        List<PhoneNumber>    batch;
        batch = new List<PhoneNumber>(from Whitemice.ZOGI.PhoneNumber p
  		                                in objects.Values.OfType<PhoneNumber>()
	  	                                where p.ParentObjectId == objectId &&
		                                    p.PhoneType == type
                                      select p);
	      if (batch.Count() == 1)
	      {
          this.ZLOG(string.Format("PhoneNumber {0} found for objectId#{1}", type, objectId));
	  	    result = batch.ElementAt(0);
	  	  } else 
          {
            this.ZLOG(string.Format("PhoneNumber {0} *NOT* found for objectId#{1}", type, objectId));
            result = null;
          }
	  	  batch = null;
	  	}
	  	return result;		
		} // end PhoneNumber
		
		internal Address GetAddress(int objectId, string type)
		{
		  IEnumerable<Address>    batch;
		  Whitemice.ZOGI.Address  result;
		  lock(this)
		  { 
  		  batch = from Address a 
  		          in objects.Values.OfType<Whitemice.ZOGI.Address>()
	  	          where a.ParentObjectId == objectId &&
		                  a.AddressType == type
                select a;
	      if (batch.Count() == 1)
	      {
	        this.ZLOG(string.Format("Address {0} found for objectId#{1}", type, objectId));
	  	    result = batch.ElementAt(0);
	  	  } else 
          {
            this.ZLOG(string.Format("Address (0) *NOT* found for objectId#{1}", type, objectId));
            result = null;
          }
	  	  batch = null;
	  	}
	  	return result;		
		} // end GetAddress
		
		internal IEnumerable<Note> GetNotesFor(Entity entity)
		{
		  IEnumerable<Note>    batch;
		  lock(this)
		  { 
  		  batch = from Note n 
  		          in objects.Values.OfType<Whitemice.ZOGI.Note>()
	  	          where n.ProjectId == entity.ObjectId ||
		                  n.AppointmentId == entity.ObjectId
                select n;
	  	}
	  	return batch;
		} // end GetNotesFor

    internal void TransferId(int objectId, Entity entity)
    {
      this.ZLOG("Transfering ID#{0} to ID#{1}", objectId, entity.ObjectId);
      lock(this)
      {
        if (objects.ContainsKey(objectId))
          objects.Remove(objectId);
        this.Store(entity);
      }
    } // end TransferId
  } // end class (Cache)
}
